package ro.sergiu.finder.ui;

import java.awt.Component;
import java.awt.Image;
import java.beans.Beans;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import ro.sergiu.finder.MetaDataTableModel;
import ro.sergiu.finder.exception.EngineRunException;
import ro.sergiu.finder.filters.FilterField;
import ro.sergiu.finder.filters.FilterPanelUI;
import ro.sergiu.finder.models.columns.ColumnModel;
import ro.sergiu.finder.models.finder.FinderModel;
import ro.sergiu.finder.models.finder.db.DbFinderModel;
import ro.sergiu.finder.models.grid.GridModel;

/**
 *
 * @author Toporjinschi
 * @param <T> Bean objects for which the search will take place
 */
public class FindDialog<T> extends javax.swing.JDialog {

    private FinderModel finderModel = null;
    private List<T> selectedObjects = new ArrayList<T>();
    private GridModel gridModel;
    private List<FilterPanelUI> filterPanelList;
    private SearchEngineSpecifications engine;

    /**
     * This constructor should be used only by de IDE. Calling this will throw
     * {@link IllegalAccessException}
     */
    public FindDialog() {
        if (!Beans.isDesignTime()) {
            throw new IllegalArgumentException("Illegal call of constructor FindDialog(). Should be used only in design time.");
        }
        initComponents();
    }

    @Override
    public void setIconImage(Image image) {
        super.setIconImage(image);
        this.search.setIcon(new ImageIcon(image));
    }

    /**
     * Creates the window for search.<br>
     *
     * @param finderModel {@link DbFinderModel} carrier for window settings
     * @param tableSettings {@link GridModel} carrier for table settings
     */
    public FindDialog(FinderModel finderModel, GridModel tableSettings) {
        this.gridModel = tableSettings;
        this.finderModel = finderModel;
        this.filterPanelList = this.finderModel.getFilters();
        prepareSearchEngine();
        initComponents();
        applyTableSettings();
        this.populateFilters();
    }

    public List<T> getSelectedObjects() {
        return this.selectedObjects;
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        queryPanels = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        searchTerm = new javax.swing.JTextField();
        search = new javax.swing.JButton();
        jPanel1 = new javax.swing.JPanel();
        splitPanel = new javax.swing.JSplitPane();
        filterPanels = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        resultTable = new javax.swing.JTable(){
            @Override
            public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
                Component c = super.prepareRenderer(renderer, row, column);
                if (!isRowSelected(row)) { //  Alternate row color
                    row++; //0 is even, because is in base 0 I have to increment row number
                    c.setBackground(row % 2 == 0 ? gridModel.getEvenBackground() : gridModel.getOddBackground());
                    c.setForeground(row % 2 == 0 ? gridModel.getEvenForeground(): gridModel.getOddForeground());
                }
                return c;
            }
        };
        buttons = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();
        filler3 = new javax.swing.Box.Filler(new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 32767));
        recCount = new javax.swing.JLabel();
        filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
        okButton = new javax.swing.JButton();
        filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 0), new java.awt.Dimension(5, 32767));
        cancelButton = new javax.swing.JButton();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setLocationByPlatform(true);
        setMinimumSize(new java.awt.Dimension(316, 282));
        setModal(true);
        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });

        queryPanels.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 5, 1, 5));
        queryPanels.setMaximumSize(new java.awt.Dimension(2147483647, 29));

        jLabel1.setText("Cauta:");

        search.setText("Cauta");
        search.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
        search.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                searchActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout queryPanelsLayout = new javax.swing.GroupLayout(queryPanels);
        queryPanels.setLayout(queryPanelsLayout);
        queryPanelsLayout.setHorizontalGroup(
            queryPanelsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(queryPanelsLayout.createSequentialGroup()
                .addGap(0, 0, 0)
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(searchTerm, javax.swing.GroupLayout.DEFAULT_SIZE, 435, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(search)
                .addGap(0, 0, 0))
        );
        queryPanelsLayout.setVerticalGroup(
            queryPanelsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(queryPanelsLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(queryPanelsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
                    .addComponent(jLabel1)
                    .addComponent(searchTerm, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(search))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        getContentPane().add(queryPanels, java.awt.BorderLayout.NORTH);

        jPanel1.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 5, 0, 5));
        jPanel1.setLayout(new java.awt.CardLayout());

        splitPanel.setDividerSize(0);
        splitPanel.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        filterPanels.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        filterPanels.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEADING, 0, 0));
        splitPanel.setTopComponent(filterPanels);

        jScrollPane1.setFocusable(false);

        resultTable.setAutoCreateRowSorter(true);
        resultTable.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                resultTableMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(resultTable);

        splitPanel.setBottomComponent(jScrollPane1);

        jPanel1.add(splitPanel, "card2");

        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);

        buttons.setBorder(javax.swing.BorderFactory.createEmptyBorder(5, 5, 5, 5));
        buttons.setMaximumSize(new java.awt.Dimension(32767, 38));
        buttons.setLayout(new javax.swing.BoxLayout(buttons, javax.swing.BoxLayout.LINE_AXIS));

        jLabel2.setText("Nr. inregistrari:");
        buttons.add(jLabel2);
        buttons.add(filler3);

        recCount.setText("0");
        buttons.add(recCount);
        buttons.add(filler2);

        okButton.setText("Selectie");
        okButton.setMaximumSize(new java.awt.Dimension(80, 24));
        okButton.setMinimumSize(new java.awt.Dimension(80, 24));
        okButton.setPreferredSize(new java.awt.Dimension(80, 24));
        okButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                okButtonActionPerformed(evt);
            }
        });
        buttons.add(okButton);
        buttons.add(filler1);

        cancelButton.setText("Renunt");
        cancelButton.setDefaultCapable(false);
        cancelButton.setMaximumSize(new java.awt.Dimension(80, 24));
        cancelButton.setMinimumSize(new java.awt.Dimension(80, 24));
        cancelButton.setPreferredSize(new java.awt.Dimension(80, 24));
        cancelButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cancelButtonActionPerformed(evt);
            }
        });
        buttons.add(cancelButton);

        getContentPane().add(buttons, java.awt.BorderLayout.SOUTH);

        setSize(new java.awt.Dimension(585, 344));
        setLocationRelativeTo(null);
    }// </editor-fold>//GEN-END:initComponents

    private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
        this.selectedObjects = null;
        this.dispose();
    }//GEN-LAST:event_cancelButtonActionPerformed

    private void searchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_searchActionPerformed
        try {
            engine.search(this.searchTerm.getText());
            resultTable.setModel(engine.getTableModel());
            setCellRenderers();
            updateRecordCount();
        } catch (EngineRunException ex) {
            Logger.getLogger(FindDialog.class.getName()).log(Level.SEVERE, null, ex);
            if (ex.hasInternalMessage()) {
                JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }//GEN-LAST:event_searchActionPerformed

    private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
        selectButton();
    }//GEN-LAST:event_okButtonActionPerformed

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        if (filterPanelList.isEmpty()) {
            return;
        }
        int noFilters = filterPanelList.size();
        double filterHeight = filterPanelList.get(0).getMaximumSize().getHeight();
        double filterWidth = filterPanelList.get(0).getMaximumSize().getWidth() + 6;
        double windowWidth = this.getWidth();
        int noCol = (int) (windowWidth / filterWidth);
        int noRow = noFilters / noCol;
        if ((((double) noFilters / noCol) - noRow) > 0) {
            noRow++;
        }
        splitPanel.setDividerLocation(((int) filterHeight * noRow) + 9);//9 is the gap between filters and table
    }//GEN-LAST:event_formComponentResized

    private void resultTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_resultTableMouseClicked
        if (evt.getClickCount() == 2) {
            selectButton();
        }
    }//GEN-LAST:event_resultTableMouseClicked

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel buttons;
    private javax.swing.JButton cancelButton;
    private javax.swing.Box.Filler filler1;
    private javax.swing.Box.Filler filler2;
    private javax.swing.Box.Filler filler3;
    private javax.swing.JPanel filterPanels;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JButton okButton;
    private javax.swing.JPanel queryPanels;
    private javax.swing.JLabel recCount;
    private javax.swing.JTable resultTable;
    private javax.swing.JButton search;
    private javax.swing.JTextField searchTerm;
    private javax.swing.JSplitPane splitPanel;
    // End of variables declaration//GEN-END:variables

    public JTable getResultTable() {
        return resultTable;
    }

    private void populateFilters() {
        filterPanels.setVisible(!filterPanelList.isEmpty());
        for (FilterPanelUI filterPanel : filterPanelList) {
            filterPanels.add(filterPanel);
        }
    }

    private void setCellRenderers() {
        TableColumnModel columnModel = this.resultTable.getColumnModel();
        List<ColumnModel> columns = this.finderModel.getVisibleColumns();
        for (int i = 0; i < columns.size(); i++) {
            ColumnModel column = columns.get(i);
            columnModel.getColumn(i).setCellRenderer(column.getTableCellRenderer());
            if (column.getWidth() != ColumnModel.NOT_DEFINED_WIDTH) {
                columnModel.getColumn(i).setWidth(column.getWidth());
            }
            if (column.getPreferredWidth() != ColumnModel.NOT_DEFINED_WIDTH) {
                columnModel.getColumn(i).setPreferredWidth(column.getPreferredWidth());
            }
            if (column.getMaxWidth() != ColumnModel.NOT_DEFINED_WIDTH) {
                columnModel.getColumn(i).setMaxWidth(column.getMaxWidth());
            }
            if (column.getMinWidth() != ColumnModel.NOT_DEFINED_WIDTH) {
                columnModel.getColumn(i).setMinWidth(column.getMinWidth());
            }
        }
    }

    private void selectButton() {
        int[] selection = this.resultTable.getSelectedRows();
        for (int i = 0; i < selection.length; i++) {
            int sel = resultTable.convertRowIndexToModel(selection[i]);
            this.selectedObjects.add(((MetaDataTableModel<T>) this.resultTable.getModel()).getBean(sel));
        }
        this.dispose();
    }

    private void updateRecordCount() {
        int recordNo = ((MetaDataTableModel) this.resultTable.getModel()).getRowCount();
        this.recCount.setText(String.valueOf(recordNo));
    }

    private void applyTableSettings() {
        resultTable.setSelectionMode(gridModel.getSelectionMode().getId());
        resultTable.setAutoResizeMode(gridModel.getAutoresizeMode().getId());

        if (!gridModel.isMovableColumns()) {
            resultTable.setRowSorter(null);
        }
        resultTable.setAutoCreateRowSorter(gridModel.isMovableColumns());
        resultTable.getTableHeader().setReorderingAllowed(gridModel.isSortableRecords());
    }

    private void prepareSearchEngine() {
        this.engine = this.finderModel.getSearchEngine();
        this.engine.setModel(finderModel);
        this.engine.setFilterFields(new ArrayList<FilterField>(this.filterPanelList));
    }

}
